package bakaserver.levelsystem;

import org.bukkit.ChatColor;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.*;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Logger;
import org.bukkit.entity.*;
import org.bukkit.event.Listener;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.event.player.PlayerRespawnEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent;

public final class levelsystem extends JavaPlugin implements Listener {
	public Logger consoleLogger = getLogger();
	public String driver, url, user, password, table;
	public Connection conn;
	public Statement statement;
	public YamlConfiguration yamlConfig;
	public File cfg;
	public double multiplyNextLevel, perLevelHealth, perLevelAmmor;
	public int LevelMax, Level1Exp, initHP;
	public boolean debug;
	public HashMap<String, Integer> hExpTable = new HashMap<String, Integer>();

	void DebugLogger(String m) {
		if (debug)
			consoleLogger.info(m);
	}

	void OutputExcpetion(String from, Exception e) {
		getLogger().info("-> In " + from);
		getLogger().info("->" + e.getMessage());
		getLogger().info("->" + e.toString());
		getLogger().info("->" + e.getLocalizedMessage());
	}

	void ExecuteSQLUpdate(String SQLCommand) throws SQLException {
		statement.close();
		conn.close();
		conn = DriverManager.getConnection(url, user, password);
		statement = conn.createStatement();
		DebugLogger(SQLCommand);
		statement.executeUpdate(SQLCommand);
	}

	ResultSet ExecuteSQLQuery(String SQLCommand) throws SQLException {
		statement.close();
		conn.close();
		conn = DriverManager.getConnection(url, user, password);
		statement = conn.createStatement();
		DebugLogger(SQLCommand);
		return statement.executeQuery(SQLCommand);
	}

	boolean CreateConfig() {
		try {
			InputStream in = getResource("config.yml");
			OutputStream out = new FileOutputStream(cfg);

			byte[] buffer = new byte[1024];
			int len;
			while ((len = in.read(buffer)) != -1) {
				out.write(buffer, 0, len);
			}

			in.close();
			out.close();
		} catch (Exception e) {
			consoleLogger.info("Can't create config.yml file.");
			return false;
		}
		return true;
	}

	boolean LoadConfig() {
		yamlConfig = new YamlConfiguration();
		try {
			yamlConfig.load(cfg);
			driver = yamlConfig.getString("sqlConfig.driver");
			url = yamlConfig.getString("sqlConfig.url");
			user = yamlConfig.getString("sqlConfig.user");
			password = yamlConfig.getString("sqlConfig.password");
			table = yamlConfig.getString("sqlConfig.table");
			Level1Exp = yamlConfig.getInt("Level.Level1Exp");
			multiplyNextLevel = yamlConfig.getDouble("Level.MultiplyNextLevel");
			perLevelHealth = yamlConfig.getDouble("Level.PerLevelAddHealth");
			perLevelAmmor = yamlConfig.getDouble("Level.PerLevelAddAmmor");
			LevelMax = yamlConfig.getInt("Level.Max");
			initHP = yamlConfig.getInt("Level.InitHealth");
			debug = yamlConfig.getBoolean("Debug");

		} catch (FileNotFoundException e) {
			consoleLogger.info("Config file not found , plugin disabled");
			setEnabled(false);
			return false;
		} catch (IOException e) {
			e.printStackTrace();
			consoleLogger
					.info("IO Exception catched , please check your harddisk");
			return false;
		} catch (InvalidConfigurationException e) {
			e.printStackTrace();
			consoleLogger
					.info("Config invalid! Check your config and reload plugin");
			return false;
		}
		return true;
	}

	boolean Reload(CommandSender sender) {
		if (!sender.hasPermission("bakalevel.reload")) {
			sender.sendMessage("权限不足");
			consoleLogger.info("Player " + sender.getName()
					+ "tried reload plugin!");
			return false;
		} else {
			try {

				statement.close();
				conn.close();
			} catch (SQLException e) {
				consoleLogger.info("Error reloading");
				return false;
			}
			LoadConfig();
			consoleLogger.info("Reload successful.");
			return true;
		}
	}

	boolean ShowHelp(CommandSender sender) {

		if (sender instanceof Player) {
			sender.sendMessage("BAKA Server LeveL System");
			if (sender.hasPermission("lvl.zero"))
				sender.sendMessage("/lvl zero [playerName] - Zero a player exp");
			if (sender.hasPermission("lvl.set"))
				sender.sendMessage("/lvl setexp [playerName] [exp] - set exp to a player");
			if (sender.hasPermission("lvl.set"))
				sender.sendMessage("/lvl setlvl [playerName] [level] - set level to a player");
			sender.sendMessage("/lvl view - view your self exp");
			if (sender.hasPermission("lvl.viewother"))
				sender.sendMessage("/lvl view [playerName] - view a player exp");
			return true;
		}
		return false;
	}

	@Override
	public void onEnable() {
		if (!getDataFolder().exists()) {
			consoleLogger
					.info("Can't load config file ,please config the plugin after use it.");
			getDataFolder().mkdir();
			CreateConfig();
		}
		cfg = new File(getDataFolder(), "config.yml");
		if (!cfg.exists()) {
			CreateConfig();
			consoleLogger.info("Config loaded fail, copy form jar.");
			setEnabled(false);
		}
		if (cfg.exists())
			LoadConfig();

		try {
			Class.forName(driver);
			conn = DriverManager.getConnection(url, user, password);
			statement = conn.createStatement();
			DatabaseMetaData meta = conn.getMetaData();
			ResultSet rs = meta.getTables(null, null, table, null);
			if (!rs.next()) {
				consoleLogger.info("The table" + table
						+ " not exist in yoru database, auto created.");
				ExecuteSQLUpdate("create table "
						+ table
						+ "(name varchar(100) not null primary key ,exp int(11))");
			}
		} catch (ClassNotFoundException e) {
			consoleLogger.info("数据库驱动加载失败 ");
			setEnabled(false);
			return;
		} catch (SQLException e) {
			consoleLogger.info("数据库连接失败");
			consoleLogger.info(e.getSQLState());
			consoleLogger.info(e.getMessage());
			consoleLogger.info(e.getLocalizedMessage());
			setEnabled(false);
			return;
		} catch (Exception e) {
			consoleLogger.info("未知错误");
			setEnabled(false);
			return;
		}
		getServer().getPluginManager().registerEvents(this, this);
		consoleLogger.info("BAKAServer Level System Started Successfuly!!");
	}

	@Override
	public void onDisable() {
		try {
			consoleLogger.info("Save level data into database..");
			Set<Entry<String, Integer>> sets = hExpTable.entrySet();
			for (Entry<String, Integer> entry : sets) {
				DebugLogger("update " + entry.getKey() + "=" + entry.getValue());
				updatePlayerExp(entry.getKey(), entry.getValue());
			}
			consoleLogger.info("Done.");
			conn.close();
			statement.close();
		} catch (SQLException e) {
			consoleLogger
					.info("SQL Database disconnect error ,check your sql config correctly.");
		} catch (Exception e) {
			consoleLogger.info("Plugins disabled!.");
		}

		consoleLogger.info("BAKAServer Level System Closed Successfully!");
	}

	@Override
	public boolean onCommand(CommandSender sender, Command cmd, String label,
			String[] args) {
		if (!(cmd.getName().equalsIgnoreCase("lvl"))) {
			return false;
		}
		DebugLogger("On commands");
		if (args.length == 0)
			return false;
		DebugLogger("call " + args[0].toLowerCase());
		switch (args[0].toLowerCase()) {
		case ("help"):
			ShowHelp(sender);
			return true;
		case ("reload"):
			Reload(sender);
			return true;
		case ("zero"):
			if (sender.isOp()) {
				setLevel(args[1], 0);
				sender.sendMessage("已将玩家" + args[1] + "的经验置零.");
				return true;
			} else {
				sender.sendMessage("权限不足");
				break;
			}
		case ("setexp"):
			if (sender.isOp()) {
				setExp(args[1], Integer.parseInt(args[2]));
				sender.sendMessage("已将玩家" + args[1] + "的经验设置为:" + args[2]);
				return true;
			} else {
				sender.sendMessage("权限不足");
				break;
			}
		case ("setlvl"):
			if (sender.isOp()) {
				setLevel(args[1], Integer.parseInt(args[2]));
				sender.sendMessage("已将玩家" + args[1] + "的等级设置为:" + args[2]);
				return true;
			} else {
				sender.sendMessage("权限不足");
				break;
			}
		case ("view"):
			if (args.length == 1) {
				if (!sender.hasPermission("lvl.dontviewself") || sender.isOp()) {
					sender.sendMessage("你目前经验为:"
							+ getExp(sender.getName())
							+ "(Lv."
							+ getLevel(sender.getName())
							+ ") 护甲等级:"
							+ (int) (getLevel(sender.getName()) * multiplyNextLevel)
							+ "(减伤:"
							+ (int) (getLevel(sender.getName()) * perLevelAmmor)
							+ ")");
					return true;
				} else {
					sender.sendMessage("权限不足");
					break;
				}
			} else if (args.length == 2) {
				if (sender.hasPermission("lvl.viewother") || sender.isOp()) {
					sender.sendMessage("玩家:" + args[1] + " 目前经验为:"
							+ getExp(args[1]) + "(Lv." + getLevel(args[1])
							+ ")");
					return true;
				} else {
					sender.sendMessage("权限不足");
					break;
				}
			} else {
				break;
			}
		default:
			break;
		}

		return true;
	}

	public int getExp(String playerName) {
		DebugLogger("getExp:" + playerName + "="
				+ GetInGameExp(playerName.toLowerCase()));
		return GetInGameExp(playerName.toLowerCase());
	}

	public int getLevel(String playerName) {

		int lvl;
		int exp = getExp(playerName);
		for (lvl = 0; exp > 0; lvl++)
			exp -= lvl * Level1Exp * multiplyNextLevel;
		lvl = lvl > 0 ? lvl - 1 : 0;
		DebugLogger("getLevel:" + playerName + "=" + lvl);
		return lvl > LevelMax ? LevelMax : lvl;
	}

	public String getFormatedLevel(String playerName) {
		DebugLogger("getFormatedLevel:" + playerName);
		return "[Lv." + getLevel(playerName) + "]";
	}

	public void addExp(String playerName, int exp) {
		DebugLogger("AddExp:" + playerName + "=" + exp);
		setExp(playerName, getExp(playerName) + exp);
		return;
	}

	public void updatePlayerExp(String playerName, int exp) {
		try {
			ExecuteSQLUpdate("update " + table + " set exp='" + exp
					+ "' where name='" + playerName + "';");
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			OutputExcpetion("AddExp", e);
		}
	}

	public void setExp(String playerName, int exp) {
		DebugLogger("SetExp:" + playerName + "=" + exp);
		/*
		 * try { ExecuteSQLUpdate( "update " + table + " exp='" + exp +
		 * "' where name='" + playerName + "';" ); } catch (SQLException e) { //
		 * TODO Auto-generated catch block OutputExcpetion("SetExp",e); }
		 */

		WriteInGameExp(playerName.toLowerCase(), exp);
		getServer().getPlayer(playerName).setMaxHealth(
				20 + (perLevelHealth * getLevel(playerName)));
		return;
	}

	public void setLevel(String playerName, int lvl) {
		int exp;
		for (exp = 0; lvl > 0; lvl--)
			exp += lvl * Level1Exp * multiplyNextLevel;
		setExp(playerName, exp);
	}

	@EventHandler
	public void playerLogin(PlayerJoinEvent event) {
		DebugLogger("Player Join");
		String playerName = event.getPlayer().getName().toLowerCase();
		if (!hExpTable.containsKey(playerName)) {
			try {
				ResultSet rs = ExecuteSQLQuery("select * from " + table
						+ " where name='" + playerName + "';");
				if (!rs.next()) {
					ExecuteSQLUpdate("insert into " + table
							+ " (name, exp) values ('" + playerName + "',"
							+ "0);");
					WriteInGameExp(playerName, 0);
					getServer()
							.getLogger()
							.info("Player:"
									+ playerName
									+ " seem first join into server till install this plugin ,inited database.");
				} else {
					WriteInGameExp(playerName, rs.getInt("exp"));
				}

				event.getPlayer().setMaxHealth(
						initHP
								+ (perLevelHealth * getLevel(event.getPlayer()
										.getName())));
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				OutputExcpetion("Login Event", e);
			}
		}
	}

	public void WriteInGameExp(String playerName, int exp) {
		hExpTable.put(playerName.toLowerCase(), exp);
	}

	public int GetInGameExp(String playerName) {
		return hExpTable.get(playerName.toLowerCase()).intValue();
	}

	@EventHandler
	public void onEntityDead(EntityDeathEvent event) {
		DebugLogger("Entity Dead.");
		if (event.getEntity().getKiller() != null) {
			Player byPlayer = event.getEntity().getKiller();
			DebugLogger("Kill by player:" + byPlayer.getName());
			if (event.getEntity() instanceof Zombie) {
				addExp(byPlayer.getName(), 3);
			} else if (event.getEntity() instanceof Skeleton) {
				addExp(byPlayer.getName(), 3);
			} else if (event.getEntity() instanceof Spider) {
				addExp(byPlayer.getName(), 3);
			} else if (event.getEntity() instanceof PigZombie) {
				addExp(byPlayer.getName(), 3);
			} else if (event.getEntity() instanceof Creeper) {
				addExp(byPlayer.getName(), 3);
			} else if (event.getEntity() instanceof CaveSpider) {
				addExp(byPlayer.getName(), 3);
				/*
				 * } else if (event.getEntity() instanceof Enderman) {
				 * addExp(byPlayer.getName(), 3);
				 */
			} else if (event.getEntity() instanceof Player) {
				Player deadPlayer = ((Player) event.getEntity());
				deadPlayer.sendMessage("你因为死亡扣除3点经验值.");
				addExp(deadPlayer.getName(), -5);
				if (getExp(deadPlayer.getName()) > Level1Exp) {
					byPlayer.sendMessage("你在PVP中打败" + deadPlayer.getName()
							+ "，得到" + Level1Exp + "经验值");
					deadPlayer.sendMessage("你在PVP中被" + byPlayer.getName()
							+ "打败，损失" + Level1Exp + "经验值");
					addExp(byPlayer.getName(), Level1Exp);
					addExp(deadPlayer.getName(), -Level1Exp);
				} else {
					byPlayer.sendMessage("对方的战斗经验不足，你无法获取经验");
					deadPlayer
							.sendMessage("你的对手太强大， 你    根    本   无   法   战  斗");
				}
			}
		} else {
			return;
		}
	}

	@EventHandler(priority=EventPriority.HIGHEST)
	public void onPlayerChat(AsyncPlayerChatEvent event) {
		DebugLogger("Player Chat");
		event.setFormat(ChatColor.LIGHT_PURPLE
				+ getFormatedLevel(event.getPlayer().getName())
				+ event.getFormat());
	}

	@EventHandler
	public void onPlayerRespawn(PlayerRespawnEvent event) {
		DebugLogger("Player Respawn");
		event.getPlayer().setMaxHealth(
				initHP
						+ (perLevelHealth * getLevel(event.getPlayer()
								.getName())));
	}

	@EventHandler
	public void onPlayerLeave(PlayerQuitEvent event) {
		DebugLogger("Player Leave");
		String playerName = event.getPlayer().getName();
		updatePlayerExp(playerName, GetInGameExp(playerName));
	}

	@EventHandler
	public void onPlayerBeenAttack(EntityDamageByEntityEvent event) {
		if (event.getEntity() instanceof Player) {
			event.setDamage(event.getDamage()
					- (perLevelAmmor * getLevel(((Player) event.getEntity())
							.getName())));
		}
	}

}
